
#include "BulletSimulator.h"

#include "QGLRenderer.h"

#ifdef UNIX
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#elif MACOSX
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#endif

#include <iostream>
#include <QtGui/QApplication>
#include <QtCore/QTimer>
#include <QtGui/QDesktopWidget>
#include <stdlib.h>
#include <math.h>
#include <limits.h>
#include <BulletDynamics/Dynamics/btRigidBody.h>

#include "StringUtil.h"
#include "defs.h"
#include "MathUtil.h"
#include "PrimitiveCreature.h"
#include "BasicCreature.h"
#include "HumanCreature.h"
#include "HalfHumanCreature.h"
#include "BasicDebug.h"
#include "QuadrupedCreature.h"

namespace jcrada {

    const double QGLRenderer::DEFAULT_CAMERA_X = 0.0;
    const double QGLRenderer::DEFAULT_CAMERA_Y = 0.8;
    const double QGLRenderer::DEFAULT_CAMERA_Z = 0.6;

    QGLRenderer::QGLRenderer(QWidget* parent, int x, int y, int w, int h)
    : QGLWidget(parent),
    _mouse_x(0), _mouse_y(0),
    _mouse_mode(MM_CAMERA),
    _sensitivity(0.5),
    _frame_rate(24),
    _cam_frame_rate(0),
    _cam_frame_count(0),
    _renderable(true),
    _show_axis(true),
    _show_center_of_mass(false),
    _show_connection_points(false),
    _show_bounding_spheres(false),
    _follow_creature(false),
    _solid_draw(false),
    _black_and_white(false),
    _render_ground(true),
    _selected_body_parts(new std::vector<btRigidBody*>()),
    _ix_rigid_body(-1),
    _left_button_pressed(false),
    _lock(new QReadWriteLock(QReadWriteLock::Recursive)) {
        setGeometry(x, y, w, h);
        setWindowTitle(tr("Creature Simulator"));
        setAutoBufferSwap(false);

        _camera_aim[0] = 0.0;
        _camera_aim[1] = 0.0;
        _camera_eye[0] = DEFAULT_CAMERA_X;
        _camera_eye[1] = DEFAULT_CAMERA_Y;
        _camera_eye[2] = DEFAULT_CAMERA_Z;

        _camera_eye_lock[0] = 0.0;
        _camera_eye_lock[1] = 0.0;
        _camera_eye_lock[2] = 0.0;

    }

    QGLRenderer::~QGLRenderer() {
        delete _lock;
    }

    void QGLRenderer::initializeGL() {
        glPointSize(3); // Points are n pixels in diameter
        glLineWidth(2);
        glEnable(GL_POINT_SMOOTH);
        glEnable(GL_LINE_SMOOTH);
        glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); // Make round points, not square points
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Antialias the lines
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glShadeModel(GL_SMOOTH);
    }

    void QGLRenderer::updateCamera() {
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(60.0, 4.0 / 3.0, 0.1, 1000.0);
        glRotated(-_camera_aim[1], 1.0, 0.0, 0.0);
        glRotated(_camera_aim[0], 0.0, 1.0, 0.0);
        glTranslated(-_camera_eye[0], -_camera_eye[1], -_camera_eye[2]);
        glMatrixMode(GL_MODELVIEW);
    }

    void QGLRenderer::paintGL() {
        //        makeCurrent();
        if (isBlackAndWhite()) {
            glClearColor(1, 1, 1, 0.5);
        } else {
            glClearColor(120.0 / 255.0, 150.0 / 255.0, 255.0 / 255.0, 0.5);
        }
        glClear(GL_COLOR_BUFFER_BIT);
        updateCamera();
        glLoadIdentity();
        glPushMatrix();
        if (isRenderGround()) {
            renderGround();
        }
        glPopMatrix();
        if (!isRenderable()) {
            glFlush();
            swapBuffers();
            return;
        }
        BulletSimulator::getSingleton().getLock().lockForRead();
        const int num_objects = BulletSimulator::getSingleton().getWorld().getNumCollisionObjects();
        for (int i = 0; i < num_objects; ++i) {
            btCollisionObject* colObj = BulletSimulator::getSingleton().getWorld().getCollisionObjectArray()[i];
            btRigidBody* body = dynamic_cast<btRigidBody*> (colObj);
            if (!body) {
                BDEBUG("Rigid body dycast failed");
            } else {
                glPushMatrix();
                render(body);
                glPopMatrix();
                if (isShowCenterOfMass()) {
                    renderCenterOfMass(body);
                }
            }
        }
        if (isShowConnectionPoints() && BulletSimulator::getSingleton().currentCreature()) {
            renderConnectionPoints(*BulletSimulator::getSingleton().currentCreature());
        }
        BulletSimulator::getSingleton().getLock().unlock();
        if (isShowAxis()) {
            renderAxis();
        }
        glFlush();
        swapBuffers();
        //        doneCurrent();
    }

    void QGLRenderer::renderCenterOfMass(btRigidBody* body) {
        glPushMatrix();
        const btTransform& com = body->getCenterOfMassTransform();
        glTranslated(com.getOrigin().x(), com.getOrigin().y(), com.getOrigin().z());
        glColor3f(0.0, 0.0, 0.0);
        glutWireSphere(0.035, 11.0, 11.0);
        glPopMatrix();
    }

    void QGLRenderer::renderConnectionPoints(Creature& creature) {
        btTransform transform;
        for (int i = 0; i < creature.getNumberOfBodyParts(); ++i) {
            for (int j = 0; j < Constraint::MAX_CONNECTION_POINTS; ++j) {
                glPushMatrix();
                Constraint::locateConnectionOrigin(creature.getBodyPart(i), j, transform);
                transform = creature.getBodyPart(i).getRigidBody()->getCenterOfMassTransform() * transform;
                glColor3f(1.0, 1.0, 0.0);
                glPointSize(8);
                glBegin(GL_POINTS);
                glVertex3f(transform.getOrigin().x(), transform.getOrigin().y(), transform.getOrigin().z());
                glEnd();
                glPopMatrix();
            }
        }
    }

    void QGLRenderer::renderAxis() {
        double limit = 10;
        glPushMatrix();
        glPointSize(15);
        glColor3f(1.0, 0.0, 0.0);
        glBegin(GL_POINTS);
        glVertex3f(0.0, 0.0, 0.0);
        glEnd();

        glColor3f(0.0, 0.0, 0.0);
        glBegin(GL_LINES);
        //Eje X
        glVertex3i(-limit, 0.0, 0.0);
        glVertex3i(limit, 0.0, 0.0);
        //Eje Y
        glVertex3i(0.0, -limit, 0.0);
        glVertex3i(0.0, limit, 0.0);
        //Eje Z
        glVertex3i(0.0, 0.0, -limit);
        glVertex3i(0.0, 0.0, limit);
        glEnd();
        glColor3f(1.0, 0.0, 0.0);
        glPopMatrix();


        for (double i = -limit; i < limit; i += 1.0) {
            //Eje X
            glPushMatrix();
            glLineWidth(1);
            glTranslated(i, 0.0, 0.0);
            glutWireSphere(0.02, 7, 7);
            glPopMatrix();
            //Eje Y
            glPushMatrix();
            glTranslated(0.0, i, 0.0);
            glutWireSphere(0.02, 7, 7);
            glPopMatrix();
            //Eje Z
            glPushMatrix();
            glTranslated(0.0, 0.0, i);
            glutWireSphere(0.02, 7, 7);
            glPopMatrix();
        }
        for (double i = -limit + 0.5; i < limit - 0.5; i += 1.0) {
            //Eje X
            glPushMatrix();
            glLineWidth(1);
            glTranslated(i, 0.0, 0.0);
            glutWireSphere(0.01, 7, 7);
            glPopMatrix();
            //Eje Y
            glPushMatrix();
            glTranslated(0.0, i, 0.0);
            glutWireSphere(0.01, 7, 7);
            glPopMatrix();
            //Eje Z
            glPushMatrix();
            glTranslated(0.0, 0.0, i);
            glutWireSphere(0.01, 7, 7);
            glPopMatrix();
        }

        //        glPushMatrix();
        //        for (float i = -limit; i < limit; i += 0.5) {
        //            if ((int)(i * 10) % 10 == 0) {
        //                glPointSize(MathUtil::triangleFunction(10,limit,MathUtil::abs(i)));
        //            } else {
        //                glPointSize(MathUtil::triangleFunction(10,limit,MathUtil::abs(i)) / 2.0);
        //            }
        //            glBegin(GL_POINTS);
        //            //            glVertex3f((float) i / 10.0, 0, 0);
        //            //            glVertex3f(0, (float) i / 10.0, 0);
        //            //            glVertex3f(0, 0, (float) i / 10.0);
        //            glVertex3f(i, 0, 0);
        //            glVertex3f(0, i, 0);
        //            glVertex3f(0, 0, i);
        //            glEnd();
        //        }
        //        glPopMatrix();
    }

    void QGLRenderer::resizeGL(int width, int height) {

        glViewport(0, 0, (GLsizei) width, (GLsizei) height);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(60.0, (GLdouble) width / (GLdouble) height, 0.1, 20.0);
        glMatrixMode(GL_MODELVIEW);
    }

    void QGLRenderer::render(btRigidBody* body) {
        btTransform trans;
        body->getMotionState()->getWorldTransform(trans);
        static GLdouble angle = 0;
        angle++;
        if (angle > 360) angle -= 360;
        //        glTranslated(trans.getOrigin().getX(), trans.getOrigin().getY(), trans.getOrigin().getZ());
        //        glRotated(MathUtil::radToDeg(trans.getRotation().getAngle()),
        //                trans.getRotation().getX(),
        //                trans.getRotation().getY(),
        //                trans.getRotation().getZ());
        btScalar m[16];
        trans.getOpenGLMatrix(m);
#ifdef BT_USE_DOUBLE_PRECISION
        glMultMatrixd(m);
#else
        glMultMatrixf(m);
#endif

        if (isFollowCreature() && ++_cam_frame_count >= getCameraFrameRate()) {
            _camera_eye[0] = _camera_eye_lock[0] + trans.getOrigin().getX();
            //            _camera_eye[1] = _camera_eye_lock[1] + trans.getOrigin().getY();
            _camera_eye[2] = _camera_eye_lock[2] + trans.getOrigin().getZ();
            _cam_frame_count = 0;
        }

        if (isShowBoundingSpheres()) {
            renderBoundingSphere(body->getCollisionShape());
        }

        if (!isSolidDraw()) {
            if (isBlackAndWhite()) {
                glColor3f(0.0, 0.0, 0.0);
            } else {
                glColor3f(1.0, 1.0, 1.0);
            }
        }
        bool is_selected = false;
        getLock().lockForRead();
        for (std::vector<btRigidBody*>::const_iterator it = getSelectedBodyParts().begin();
                it != getSelectedBodyParts().end(); ++it) {
            if (body == *it) {
                is_selected = true;
                break;
            }
        }
        getLock().unlock();
        if (is_selected) {
            if (isBlackAndWhite()) {
                glColor3f(0.5, 0.5, 0.5);
            } else {
                glColor3f(0.0, 0.0, 0.0);
            }
        }
        glLineWidth(1);
        switch (body->getCollisionShape()->getShapeType()) {
            case SPHERE_SHAPE_PROXYTYPE:
                if (isSolidDraw() && !is_selected) {
                    glColor3f(255.0 / 255.0, 0.0, 0.0);
                }
                renderSphere(body->getCollisionShape());
                break;
            case BOX_SHAPE_PROXYTYPE:
                if (isSolidDraw() && !is_selected) {
                    glColor3f(0, 255.0 / 255.0, 0.0);
                }
                renderBox(body->getCollisionShape());
                break;
            case CAPSULE_SHAPE_PROXYTYPE:
                if (isSolidDraw() && !is_selected) {
                    glColor3f(0.0, 0.0, 255.0 / 255.0);
                }
                renderCapsule(body->getCollisionShape());
                break;
            case CYLINDER_SHAPE_PROXYTYPE:
                if (isSolidDraw() && !is_selected) {
                    glColor3f(1.0, 1.0, 1.0);
                }
                renderCylinder(body->getCollisionShape());
                break;
            case CONE_SHAPE_PROXYTYPE:
                if (isSolidDraw() && !is_selected) {
                    glColor3f(0.5, 0.5, 0.5);
                }
                renderCone(body->getCollisionShape());

                break;
            case STATIC_PLANE_PROXYTYPE: break;
            default:
                BDEBUG("shape draw not implemented for " + body->getCollisionShape()->getName());
        }
    }

    void QGLRenderer::renderBoundingSphere(const btCollisionShape* shape) {
        btVector3 center;
        btScalar radius;
        shape->getBoundingSphere(center, radius);
        glPushMatrix();
        glColor3f(0.5, 0.5, 0.5);
        glLineWidth(1);
        glTranslated(center.x(), center.y(), center.z());
        glutWireSphere(radius, 10.0, 10.0);
        glPopMatrix();
    }

    void QGLRenderer::renderSphere(const btCollisionShape *sphere) {
        const btSphereShape* shape = dynamic_cast<const btSphereShape*> (sphere);
        if (!shape) {
            BDEBUG("dynamic_cast<sphere> failed");
            return;
        }
        if (isSolidDraw()) {
            glutSolidSphere(shape->getRadius(), 10.0, 10.0);
        } else {
            glutWireSphere(shape->getRadius(), 10.0, 10.0);
        }
    }

    void QGLRenderer::renderBox(const btCollisionShape *cube) {
        const btBoxShape* shape = dynamic_cast<const btBoxShape*> (cube);
        if (!shape) {
            BDEBUG("dynamic_cast<box> failed");
            return;
        }
        if (isSolidDraw()) {
            glBegin(GL_POLYGON);
        } else {
            glBegin(GL_LINES);
        }
        btVector3 a, b;
        for (int i = 0; i < shape->getNumEdges(); ++i) {
            shape->getEdge(i, a, b);
            glVertex3d(a.getX(), a.getY(), a.getZ());
            glVertex3d(b.getX(), b.getY(), b.getZ());
        }
        glEnd();
    }

    void QGLRenderer::renderCone(const btCollisionShape *cone) {
        const btConeShape* shape = dynamic_cast<const btConeShape*> (cone);
        if (!shape) {
            BDEBUG("dynamic_cast<cone> failed");
            return;
        }
        glPushMatrix();
        switch (shape->getConeUpIndex()) {
            case 0:
                glRotatef(90.0, 0.0, 1.0, 0.0);
                glTranslatef(0.0, 0.0, -shape->getHeight() / 2.0);
                break;
            case 1:
                glRotatef(-90.0, 1.0, 0.0, 0.0);
                glTranslatef(0.0, 0.0, -shape->getHeight() / 2.0);
                break;
            case 2:
                glTranslatef(0.0, 0.0, -shape->getHeight() / 2.0);
                break;
            default: break;
        }
        if (isSolidDraw()) {
            glutSolidCone(shape->getRadius(), shape->getHeight(), 10, 10);
        } else {
            glutWireCone(shape->getRadius(), shape->getHeight(), 10, 10);
        }
        glPopMatrix();
    }

    void QGLRenderer::renderCylinder(const btCollisionShape *cylinder) {
        const btCylinderShape* shape = dynamic_cast<const btCylinderShape*> (cylinder);
        if (!shape) {
            BDEBUG("dynamic_cast<cylinder> failed");
            return;
        }
        glPushMatrix();
        double half_height = shape->getHalfExtentsWithoutMargin()[shape->getUpAxis()];
        switch (shape->getUpAxis()) {
            case 0:
                glRotatef(-90.0, 0.0, 1.0, 0.0);
                glTranslatef(0.0, 0.0, -half_height);
                break;
            case 1:
                glRotatef(-90.0, 1.0, 0.0, 0.0);
                glTranslatef(0.0, 0.0, -half_height);
                break;
            case 2:
                glTranslatef(0.0, 0.0, -half_height);
                break;
            default:
                assert(0);
        }
        GLUquadricObj *quadObj = gluNewQuadric();

        //The gluCylinder subroutine draws a cylinder that is oriented along the z axis.
        //The base of the cylinder is placed at z = 0; the top of the cylinder is placed at z=height.
        //Like a sphere, the cylinder is subdivided around the z axis into slices and along the z axis into stacks.

        if (isSolidDraw()) {
            gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
        } else {
            gluQuadricDrawStyle(quadObj, (GLenum) GLU_LINE);
        }
        gluQuadricNormals(quadObj, (GLenum) GLU_SMOOTH);

        gluDisk(quadObj, 0, shape->getRadius(), 8, 8);
        gluCylinder(quadObj, shape->getRadius(), shape->getRadius(), 2.f * half_height, 10, 10);
        glTranslatef(0.0, 0.0, 2. * half_height);
        glRotatef(-180.0, 0.0, 1.0, 0.0);
        gluDisk(quadObj, 0, shape->getRadius(), 8, 8);
        gluDeleteQuadric(quadObj);
        glPopMatrix();

    }

    void QGLRenderer::renderCapsule(const btCollisionShape *capsule) {
        const btCapsuleShape * shape = dynamic_cast<const btCapsuleShape*> (capsule);
        if (!shape) {
            BDEBUG("dynamic_cast<capsule> failed");
            return;
        }
        glPushMatrix();
        double half_height = shape->getHalfHeight();
        switch (shape->getUpAxis()) {
            case 0:
                glRotatef(-90.0, 0.0, 1.0, 0.0);
                glTranslatef(0.0, 0.0, -half_height);
                break;
            case 1:
                glRotatef(-90.0, 1.0, 0.0, 0.0);
                glTranslatef(0.0, 0.0, -half_height);
                break;
            case 2:
                glTranslatef(0.0, 0.0, -half_height);

                break;
            default: assert(0);
        }
        GLUquadricObj *quadObj = gluNewQuadric();

        //The gluCylinder subroutine draws a cylinder that is oriented along the z axis.
        //The base of the cylinder is placed at z = 0; the top of the cylinder is placed at z=height.
        //Like a sphere, the cylinder is subdivided around the z axis into slices and along the z axis into stacks.

        if (isSolidDraw()) {
            gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
        } else {
            gluQuadricDrawStyle(quadObj, (GLenum) GLU_LINE);
        }
        gluQuadricNormals(quadObj, (GLenum) GLU_SMOOTH);

        if (isSolidDraw()) {
            glutSolidSphere(shape->getRadius(), 8, 8);
        } else {
            glutWireSphere(shape->getRadius(), 8, 8);
        }
        gluCylinder(quadObj, shape->getRadius(), shape->getRadius(), 2.f * half_height, 10, 10);
        glTranslatef(0.0, 0.0, 2. * half_height);
        glRotatef(-180.0, 0.0, 1.0, 0.0);
        if (isSolidDraw()) {
            glutSolidSphere(shape->getRadius(), 8, 8);
        } else {
            glutWireSphere(shape->getRadius(), 8, 8);
        }
        gluDeleteQuadric(quadObj);
        glPopMatrix();
    }

    void QGLRenderer::renderGround() {
        double limit = 3000;
        glPushMatrix();
        if (isBlackAndWhite()) {
            glColor3f(1, 1, 1);
        } else {
            glColor3f(0.0 / 255.0, 100.0 / 255.0, 0.0);
        }
        glBegin(GL_QUADS);
        glNormal3f(0, 0, 1);
        //        glTexCoord2f(-gsize * ground_scale + ground_ofsx,
        //                -gsize * ground_scale + ground_ofsy );
        glVertex3f(-limit, 0, limit);
        //        glTexCoord2f( gsize * ground_scale + ground_ofsx,
        //                -gsize * ground_scale + ground_ofsy );
        glVertex3f(limit, 0, limit);
        //        glTexCoord2f( gsize * ground_scale + ground_ofsx,
        //                gsize * ground_scale + ground_ofsy );
        glVertex3f(limit, 0, -limit);
        //        glTexCoord2f(-gsize * ground_scale + ground_ofsx,
        //                gsize * ground_scale + ground_ofsy );
        glVertex3f(-limit, 0, -limit);
        glEnd();
        glPopMatrix();

        glPushMatrix();
        float div = 10.0;
        glLineWidth(2);
        glBegin(GL_LINES);
        //        glColor3f(27.0 / 255.0, 15.0 / 255.0, 8.0 / 255.0);
        glColor3f(0.5, 0.5, 0.5);
        for (double i = -limit / div; i < limit / div; i += 1.0) {
            glVertex3f(i, 0, -limit / div);
            glVertex3f(i, 0, limit / div);
            glVertex3f(-limit / div, 0, i);
            glVertex3f(limit / div, 0, i);
        }
        glEnd();
        glPopMatrix();


        //        glDisable( GL_FOG );
    }

    void QGLRenderer::moveCamera(eCameraMove move, GLdouble distance) {
        switch (move) {
            case CM_ADVANCE:
                _camera_eye[0] += distance * -sin(Math::degToRad(_camera_aim[0]));
                _camera_eye[2] += distance * cos(Math::degToRad(_camera_aim[0]));
                break;
            case CM_SLIDE:
                _camera_eye[0] += distance * -sin(Math::degToRad(_camera_aim[0] + 90));
                _camera_eye[2] += distance * cos(Math::degToRad(_camera_aim[0] + 90));
                break;
            case CM_AIM_VERTICAL:
                _camera_aim[1] += distance;
                if (_camera_aim[1] < -360.0) {
                    _camera_aim[1] += 360;
                }
                if (_camera_aim[1] >= 360) {
                    _camera_aim[1] -= 360;
                }
                break;
            case CM_AIM_HORIZONTAL:
                _camera_aim[0] += distance;
                if (_camera_aim[0] < -360.0) {
                    _camera_aim[0] += 360;
                }
                if (_camera_aim[0] >= 360) {
                    _camera_aim[0] -= 360;
                }
                break;
            case CM_RAISE:
                _camera_eye[1] += distance;

                break;
            default:
                assert(0);
        }
    }


    //Mouse Events:

    void QGLRenderer::selectBodyPart(btRigidBody* body_part, bool maintain_selection) {
        if (!maintain_selection) {
            deselectBodyPart(NULL);
        }
        bool exists = false;
        getLock().lockForRead();
        for (std::vector<btRigidBody*>::const_iterator it = getSelectedBodyParts().begin();
                it != getSelectedBodyParts().end(); ++it) {
            if (*it == body_part) {
                exists = true;
                break;
            }
        }
        getLock().unlock();
        if (!exists) {
            getLock().lockForWrite();
            getSelectedBodyParts().push_back(body_part);
            getLock().unlock();
        }
    }

    void QGLRenderer::deselectBodyPart(const btRigidBody* body_part) {
        getLock().lockForWrite();
        if (body_part == NULL) {
            getSelectedBodyParts().clear();
        }
        for (std::vector<btRigidBody*>::iterator it = getSelectedBodyParts().begin();
                it != getSelectedBodyParts().end(); ++it) {
            if (*it == body_part) {
                getSelectedBodyParts().erase(it);
                break;
            }
        }
        getLock().unlock();
    }

    void QGLRenderer::dragBody(double fx, double fy, double fz) {
        btRigidBody* body = NULL;
        for (std::vector<btRigidBody*>::iterator it = getSelectedBodyParts().begin();
                it != getSelectedBodyParts().end(); ++it) {
            body = *it;
            body->activate(true);
            btVector3 impulse(fx, fy, fz);
            switch (getMouseMode()) {
                case MM_TRANSLATIONAL:
                    body->applyCentralImpulse(impulse);
                    break;
                case MM_ROTATIONAL:
                    body->applyTorqueImpulse(impulse);
                    break;
                default: assert(0);
            }
        }
    }

    void QGLRenderer::mousePressEvent(QMouseEvent * event) {
        if (event->buttons() == Qt::LeftButton) {
            setLeftButtonPressed(true);
        }
        switch (getMouseMode()) {
            case MM_CAMERA:
                _mouse_x = event->x();
                _mouse_y = event->y();
                break;
            case MM_TRANSLATIONAL:
                _mouse_x = event->x();
                _mouse_y = event->y();
                break;
            case MM_ROTATIONAL:
                _mouse_x = event->x();
                _mouse_y = event->y();

                break;
            default:
                BDEBUG(TO_STRING(getMouseMode()));
                assert(0);
        }
        //        event->accept();
    }

    void QGLRenderer::mouseReleaseEvent(QMouseEvent * event) {
        switch (getMouseMode()) {
            case MM_CAMERA: break;
            case MM_TRANSLATIONAL: break;
            case MM_ROTATIONAL: break;
            default: assert(0);
        }
        if (event->buttons() != Qt::LeftButton) {
            setLeftButtonPressed(false);
        }
        event->accept();
    }

    void QGLRenderer::mouseMoveEvent(QMouseEvent * event) {
        switch (getMouseMode()) {
            case MM_CAMERA:
            {
                if (event->buttons() == (Qt::LeftButton + Qt::RightButton)) {
                    moveCamera(CM_SLIDE, (_mouse_x - event->x()) * getSensitivity());
                } else if (event->buttons() == Qt::LeftButton) {
                    moveCamera(CM_AIM_HORIZONTAL, -(_mouse_x - event->x()) * getSensitivity());
                    moveCamera(CM_AIM_VERTICAL, (_mouse_y - event->y()) * getSensitivity());
                } else if (event->buttons() == Qt::RightButton) {
                    moveCamera(CM_RAISE, (_mouse_y - event->y()) * getSensitivity());
                } else if (event->buttons() == Qt::NoButton) {
                    BDEBUG("No Button");
                } else {
                }
                break;
            }
            case MM_TRANSLATIONAL:
            {
                if (event->buttons() == Qt::LeftButton) {
                    dragBody(-(_mouse_x - event->x()) * getSensitivity() * 2,
                            (_mouse_y - event->y()) * getSensitivity() * 2, 0);
                } else if (event->buttons() == Qt::RightButton) {
                    //                    dragBody(-(_mouse_x - event->x()) * getSensitivity()_factor * 2, 0,
                    //                            -(_mouse_y - event->y()) * getSensitivity()_factor * 2);
                    dragBody(0, 0, -(_mouse_y - event->y()) * getSensitivity() * 2);
                }
                break;
            }
            case MM_ROTATIONAL:
            {
                if (event->buttons() == Qt::LeftButton) {
                    dragBody(-(_mouse_y - event->y()) * getSensitivity() * 2,
                            -(_mouse_x - event->x()) * getSensitivity() * 2, 0);
                } else if (event->buttons() == Qt::RightButton) {

                    dragBody(0, 0,
                            (_mouse_x - event->x()) * getSensitivity() * 2);
                }
                break;
            }
            default:
                assert(0);
        }
        _mouse_x = event->x();
        _mouse_y = event->y();
        event->accept();
    }

    void QGLRenderer::wheelEvent(QWheelEvent* event) {
        int num_degrees = event->delta() / 8;
        int num_steps = num_degrees / 15;
        switch (getMouseMode()) {
            case MM_CAMERA:
                if (event->orientation() == Qt::Horizontal) {
                    moveCamera(CM_SLIDE, num_steps * 0.1);
                } else {
                    if (isLeftButtonPressed()) {
                        moveCamera(CM_RAISE, num_steps * 0.1);
                    } else {
                        moveCamera(CM_ADVANCE, -num_steps * 0.1);
                    }
                }
                break;
            case MM_TRANSLATIONAL:
                if (event->orientation() == Qt::Horizontal) {
                    dragBody(-num_steps, 0, 0);
                } else {
                    dragBody(0, 0, -num_steps);
                }
                break;
            case MM_ROTATIONAL:
                if (event->orientation() == Qt::Horizontal) {
                    dragBody(0, 0, num_steps);
                } else {
                    dragBody(-num_steps, 0, 0);
                }
                break;
            default:
                BDEBUG(TO_STRING(getMouseMode()));
                assert(0);
        }
        event->accept();
    }

    int QGLRenderer::main(int argc, char** argv) {
        QApplication app(argc, argv);
        BulletSimulator& _simulator = BulletSimulator::getSingleton();
#ifdef MACOSX
        _simulator.setTimestepsPerSecond(24);
#else
        _simulator.setTimestepsPerSecond(6);
#endif
        _simulator.initPhysics();
        int x = 0, y = 0, width = 640, height = 480;
        x = (QApplication::desktop()->width() / 2.0) - (width / 2.0);
        y = (QApplication::desktop()->height() / 2.0) - (height / 2.0);
        QGLRenderer r(NULL, x, y, width, height);
        //        QObject::connect(&_simulator, SIGNAL(render()), &r, SLOT(updateGL()), Qt::DirectConnection);
        QObject::connect(&app, SIGNAL(aboutToQuit()), &r, SLOT(stop()), Qt::DirectConnection);
        r.show();
        //        r.start();

        return app.exec();
    }
}
